package dwl.graduation.design.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import dwl.graduation.design.config.SnowflakeConfig;
import dwl.graduation.design.domain.*;
import dwl.graduation.design.mapper.ApplyFurnitureMapper;
import dwl.graduation.design.mapper.ApplyInfoMapper;
import dwl.graduation.design.mapper.FurnitureInfoMapper;
import dwl.graduation.design.mapper.UserInfoMapper;
import dwl.graduation.design.service.WorkService;
import dwl.graduation.design.utils.*;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service("WorkService")
public class WorkServiceImpl implements WorkService {

    @Autowired
    private SnowflakeConfig snowflakeConfig;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private FurnitureInfoMapper furnitureInfoMapper;

    @Autowired
    private ApplyInfoMapper applyInfoMapper;

    @Autowired
    private ApplyFurnitureMapper applyFurnitureMapper;

    @Autowired
    private UserInfoMapper userInfoMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ProcessInstanceDTO startProcess(StartApplyRequest startApplyRequest) {
        Long applyId = snowflakeConfig.snowflakeId();
        Map<String, Object> variables = new HashMap<>();
        variables.put("resourceId", applyId);
        List<String> furnIdList = Arrays.asList(startApplyRequest.getFurnIdList().split(","));
        ApplyInfo applyInfo = new ApplyInfo();
        applyInfo.setApplyId(applyId);
        applyInfo.setProcess((short) Integer.parseInt(startApplyRequest.getFlowCategory()));
        applyInfo.setStep(TaskStep.DEPARTMENT.getIndex());
        applyInfo.setStatus(TaskStatus.Flowing.getIndex());
        applyInfo.setHandlerId(userInfoMapper.findUserByUserName(startApplyRequest.getDepartmentUserName()).getId());
        applyInfo.setSponsorId(userInfoMapper.findUserByUserName(startApplyRequest.getUserName()).getId());
        applyInfoMapper.insertSelective(applyInfo);
        for (String furnId : furnIdList) {
            ApplyFurniture applyFurniture = new ApplyFurniture();
            applyFurniture.setApplyId(applyId);
            applyFurniture.setFurnId(furnId);
            applyFurnitureMapper.insertSelective(applyFurniture);
        }
        variables.put("departmentApproval", startApplyRequest.getDepartmentUserName());
        variables.put("POApproval", startApplyRequest.getDefaultPOUserName());
        variables.put("seniorPOApproval", startApplyRequest.getSeniorPOUserName());
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(ConstantValues.FLOWABLE_PROCESS_BPMN_KEY, variables);
        return ProcessInstanceDTO.builder()
                .processDeploymentId(instance.getDeploymentId()).processInstanceId(instance.getProcessInstanceId()).build();
    }

    @Override
    public PageResult getTaskList(String userName) {
        List<TaskResponse> responseList = new ArrayList<>();
        List<org.flowable.task.api.Task> tasks = taskService.createTaskQuery().taskAssignee(userName).orderByTaskCreateTime().desc().list();
        for (Task task : tasks) {
            TaskResponse taskResponse = new TaskResponse();
            String taskId = task.getId();
            Map<String, Object> processVariables = taskService.getVariables(taskId);
            Long applyId = (Long) processVariables.get("resourceId");
            ApplyInfoExample applyInfoExample = new ApplyInfoExample();
            applyInfoExample.createCriteria().andApplyIdEqualTo(applyId);
            ApplyInfo applyInfo = applyInfoMapper.selectByExample(applyInfoExample).get(0);
            taskResponse.setProcess(ProcessEnum.getName(applyInfo.getProcess()));
            taskResponse.setApplyId(applyId+"");
            taskResponse.setStep(TaskStep.getName(applyInfo.getStep()));
            taskResponse.setTaskId(taskId);
            taskResponse.setStatus(TaskStatus.getName(applyInfo.getStatus()));
            taskResponse.setComment(applyInfo.getComment());
            responseList.add(taskResponse);
        }
        return PageResult.builder().total((long)responseList.size()).rows(responseList).build();
    }

    @Override
    public Result approvedTask(TaskRequest taskRequest) {
        Map<String, Object> taskVariables = new HashMap<>();
        taskVariables.put("approved", taskRequest.getApproved());
        ApplyInfoExample applyInfoExample = new ApplyInfoExample();
        applyInfoExample.createCriteria().andApplyIdEqualTo(taskRequest.getApplyId());
        ApplyInfo applyInfo = applyInfoMapper.selectByExample(applyInfoExample).get(0);
        String approvalName = taskRequest.getApprovalName();
        Map<String, Object> processVariables = taskService.getVariables(taskRequest.getTaskId());
        if (TaskStep.DEPARTMENT.getIndex().equals(applyInfo.getStep())){
            if(!taskRequest.getApprovalName().equals(processVariables.get("departmentApproval"))){
                return new Result(false, MessageConstant.APPROVED_TASK_FAIL);
            }
        }
        if (TaskStep.PO.getIndex().equals(applyInfo.getStep())){
            if(!taskRequest.getApprovalName().equals(processVariables.get("POApproval"))){
                return new Result(false, MessageConstant.APPROVED_TASK_FAIL);
            }
        }
        if (TaskStep.SENIORPO.getIndex().equals(applyInfo.getStep())){
            if(!taskRequest.getApprovalName().equals(processVariables.get("seniorPOApproval"))){
                return new Result(false, MessageConstant.APPROVED_TASK_FAIL);
            }
        }
        applyInfo.setComment(taskRequest.getComment());
        Short step = applyInfo.getStep();
        if (taskRequest.getApproved().equals("Y")) {
            applyInfo.setStep((short) (step+1));
            applyInfo.setLastStep(step);
        }else{
            applyInfo.setStep(TaskStep.FAIL.getIndex());
            applyInfo.setLastStep(step);
            applyInfo.setStatus(TaskStatus.Filed.getIndex());
        }
        applyInfoMapper.updateByExample(applyInfo,applyInfoExample);
        //审核结果和审核意见都封装为JSON然后放在评论里，后续需要进行逆操作。
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, String> map = new HashMap<String, String>();
        map.put("approved", taskRequest.getApproved());
        map.put("comment", taskRequest.getComment());
        getResult(applyInfo);
        try {
            String json = objectMapper.writeValueAsString(map);
            taskService.addComment(taskRequest.getTaskId(), null, json);
            taskService.complete(taskRequest.getTaskId(), taskVariables);
            return new Result(true, MessageConstant.APPROVED_TASK_SUCCESS);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private void getResult(ApplyInfo applyInfo) {
        Short step = applyInfo.getStep();
        if (step.equals(TaskStep.FAIL.getIndex())){
            try {

                createFailFile(applyInfo.getSponsorId(),applyInfo.getComment(),applyInfo.getApplyId());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (step.equals(TaskStep.SUCCESS.getIndex())){
            ApplyInfoExample applyInfoExample = new ApplyInfoExample();
            applyInfoExample.createCriteria().andApplyIdEqualTo(applyInfo.getId());
            ApplyFurnitureExample applyFurnitureExample = new ApplyFurnitureExample();
            applyFurnitureExample.createCriteria().andApplyIdEqualTo(applyInfo.getId());
            applyInfoMapper.updateByExampleSelective(ApplyInfo.builder()
                    .status(TaskStatus.Filed.getIndex()).build(),applyInfoExample);
            List<ApplyFurniture> applyFurnitures = applyFurnitureMapper.selectByExample(applyFurnitureExample);
            List<String> furnIdList = applyFurnitures.stream().map(ApplyFurniture::getFurnId).collect(Collectors.toList());
            for(String furnId:furnIdList){
                FurnitureInfoExample furnitureInfoExample = new FurnitureInfoExample();
                furnitureInfoExample.createCriteria().andFurnIdEqualTo(Integer.parseInt(furnId));
                FurnitureInfo furnitureInfo = furnitureInfoMapper.selectByExample(furnitureInfoExample).get(0);
                FurnitureInfo info = new FurnitureInfo();
                info.setVersion(furnitureInfo.getVersion()+1);
                if (applyInfo.getProcess().equals(ProcessEnum.NEW.getIndex())){
                    info.setFurnYear(new Date());
                }
                furnitureInfoMapper.updateByExampleSelective(info,furnitureInfoExample);
            }
            try {
                createSuccessFile(applyInfo.getSponsorId(),applyInfo.getComment(),applyInfo.getApplyId());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private void createFailFile(Integer userId,String comment,Long applyId) throws IOException {
        String fileName = "D:\\"+applyId+".txt";
        Path path = Paths.get(fileName);
        try(BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)){
            writer.write(userId+"发起的任务:"+applyId+"被拒绝，备注"+comment);
        }
    }

    private void createSuccessFile(Integer userId,String comment,Long applyId) throws IOException {
        String fileName = "D:\\"+applyId+".txt";
        Path path = Paths.get(fileName);
        try(BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)){
            writer.write(userId+"发起的任务:"+applyId+"被通过，备注"+comment);
        }
    }

    @Override
    public Map<String, Object> getProcessStatus(String processInstanceId) {
        Map<String, Object> vars = queryProcessVariables(processInstanceId);
        List<ProcessStatusDTO> status = queryProcessStatus(processInstanceId);
        String approved = "N";//这里"Y"代表审批同意，"N"代表审批不同意
        if (status.size() >= 2) {
            approved = "Y";
        }
        for (ProcessStatusDTO s : status) {
            if (null == s.getApproved() || s.getApproved().equals("N")) {
                approved = "N";
            }
        }
        vars.put("approved", approved);

        Map<String, Object> ret = new HashMap<String, Object>();
        ret.put("param", vars);
        ret.put("status", status);
        return ret;
    }

    private List<ProcessStatusDTO> queryProcessStatus(String processInstanceId) {
        List<ProcessStatusDTO> result = new ArrayList<ProcessStatusDTO>();

        List<HistoricTaskInstance> htis = historyService.createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId).list();
        if (htis.size() <= 0) {
            throw new RuntimeException("ProcessEnum instance [" + processInstanceId + "] not exist");
        }

        for (HistoricTaskInstance hti : htis) {
            String taskId = hti.getId();
            String taskName = hti.getName();
            String assignee = hti.getAssignee();
            Date createTime = hti.getCreateTime();
            String comment = null;
            String approved = null;
            List<Comment> comments = taskService.getTaskComments(taskId);
            if (comments.size() > 0) {
                comment = comments.get(0).getFullMessage();

                if (null != comment) {
                    //这里进行评论的JSON数据的逆操作提取数据
                    ObjectMapper mapper = new ObjectMapper();
                    try {
                        @SuppressWarnings("unchecked")
                        Map<String, Object> data = mapper.readValue(comment, Map.class);
                        approved = data.get("approved").toString();
                        comment = data.get("comment").toString();
                    } catch (Exception e) {
                        throw new RuntimeException("error is" + e.toString());
                    }
                }
            }

            ProcessStatusDTO pd = new ProcessStatusDTO();
            pd.setTaskName(taskName);
            pd.setAssignee(assignee);
            pd.setCreateTime(createTime);
            pd.setApproved(approved);
            pd.setComment(comment);

            result.add(pd);
        }

        return result;
    }

    private Map<String, Object> queryProcessVariables(String processInstanceId) {
        List<HistoricVariableInstance> hvis = historyService.createHistoricVariableInstanceQuery()
                .processInstanceId(processInstanceId).list();
        if (hvis == null) {
            throw new RuntimeException("ProcessEnum instance [" + processInstanceId + "] not exist");
        }

        Map<String, Object> ret = new HashMap<String, Object>();
        for (HistoricVariableInstance var : hvis) {
            ret.put(var.getVariableName(), var.getValue());
        }
        return ret;
    }

    @Override
    public List<HiTaskResponse> getHiTaskByUser(String userName) {
        List<HiTaskResponse> result = new ArrayList<>();
        List<HistoricActivityInstance> activities = historyService.createHistoricActivityInstanceQuery()
                .taskAssignee(userName).finished().orderByHistoricActivityInstanceEndTime().desc().list();
        for (HistoricActivityInstance h : activities) {
            HiTaskResponse d = new HiTaskResponse();
            d.setProcessInstanceId(h.getProcessInstanceId());
            d.setTaskId(h.getTaskId());
            d.setStartTime(h.getStartTime());
            d.setEndTime(h.getEndTime());

            result.add(d);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void insertFurniture(InsertFurnitureRequest insertFurnitureRequest) throws ParseException {
        FurnitureInfo furnitureInfo = new FurnitureInfo();
        furnitureInfo.setFurnId(Integer.parseInt(insertFurnitureRequest.getFurnId()));
        furnitureInfo.setFurnDept(insertFurnitureRequest.getFurnDept());
        furnitureInfo.setFurnArea(insertFurnitureRequest.getFurnArea());
        furnitureInfo.setFurnLoc(insertFurnitureRequest.getFurnLoc());
        furnitureInfo.setFurnLocDtl(insertFurnitureRequest.getFurnLocDtl());
        furnitureInfo.setFurnCategory(insertFurnitureRequest.getFurnCategory());
        furnitureInfo.setFurnName(insertFurnitureRequest.getFurnName());
        furnitureInfo.setFurnNo(insertFurnitureRequest.getFurnNo());
        furnitureInfo.setFurnCode(insertFurnitureRequest.getFurnCode());
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        furnitureInfo.setFurnYear(format.parse(insertFurnitureRequest.getFurnYear()));
        furnitureInfo.setFurnStatus(insertFurnitureRequest.getFurnStatus());
        furnitureInfo.setVersion(Integer.parseInt(insertFurnitureRequest.getVersion()));
        furnitureInfoMapper.insertSelective(furnitureInfo);
    }

    @Override
    public PageResult findPageList(QueryPageBean queryPageBean) {
        if (!Objects.isNull(queryPageBean.getCurrentPage()) && !Objects.isNull(queryPageBean.getPageSize())) {
            queryPageBean.setCurrentPage((queryPageBean.getCurrentPage() - 1) * queryPageBean.getPageSize());
        }
        List<FurnitureInfo> list = furnitureInfoMapper.getPageList(queryPageBean);
        Long count = furnitureInfoMapper.getListSize(queryPageBean);
        return PageResult.builder().rows(list).total(count).build();
    }


}
